#include "com_zhc_sandbox_SandBox.h"
/* JNI入口，检查并运行代码 */
JNIEXPORT jobject

JNICALL Java_com_zhc_sandbox_SandBox_exec
        (JNIEnv *env, jclass clazz, jobject jConfig) {
    struct result res;
    struct config cfg;
    parse_config(env,jConfig,&cfg);
    run(&cfg,&res);
    return get_result_object(env,res);
}
void load_jvm_seccomp() {
    // 创建程序执行的 seccomp 规则
    scmp_filter_ctx ctx;
    // 如果非受信程序越权调用其他的系统调用直接杀死程序
    ctx = seccomp_init(SCMP_ACT_KILL);
    // 允许必要的系统调用
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(access), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(arch_prctl), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_getres), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clone), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(close), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(connect), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(execve), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fchdir), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fcntl), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(fstat), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(ftruncate), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(futex), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getcwd), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getdents), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(geteuid), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getpid), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(getuid), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kexec_load), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(gettid), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(kill), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lstat), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mkdir), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(openat), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prctl), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pread64), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(prlimit64), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(pselect6), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(read), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(readlink), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigaction), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigprocmask), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(rt_sigreturn), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sched_getaffinity), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sched_yield), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_robust_list), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(set_tid_address), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(socket), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(stat), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(sysinfo), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(uname), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(unlink), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(writev), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(clock_gettime), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(lseek), 0);

    // 补充其他必要的系统调用
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(brk), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mmap), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(munmap), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(mprotect), 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 24, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 32, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 34, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 35, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 48, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 61, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 78, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 79, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 93, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 165, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 233, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 260, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 278, 0);
    seccomp_rule_add(ctx, SCMP_ACT_ALLOW, 293, 0);

    // 加载 seccomp 规则
    if (seccomp_load(ctx) < 0) {
        perror("seccomp_load");
        exit(1);
    }
}
void printf_result(struct result res) {
    printf("cpu time: %u\n", res.cpu_time);
    printf("real time: %u\n", res.real_time);
    printf("memory: %lu\n", res.memory);
    printf("signal: %d\n", res.signal);
    printf("exit code: %d\n", res.exit_code);
    printf("result: %d\n", res.result);

}
char *read_file(const char *filename) {
    FILE *file = fopen(filename, "r");
    if (file == NULL) {
        perror("Failed to open file");
        return NULL;
    }

    fseek(file, 0, SEEK_END);
    long file_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    char *buffer = (char *) malloc(file_size + 1);
    if (buffer == NULL) {
        perror("Failed to allocate buffer");
        fclose(file);
        return NULL;
    }

    size_t read_size = fread(buffer, 1, file_size, file);
    if (read_size != file_size) {
        perror("Failed to read file");
        free(buffer);
        fclose(file);
        return NULL;
    }

    buffer[file_size] = '\0'; // 确保缓冲区以空字符结尾

    fclose(file);
    return buffer;
}
void *timeout_killer(void *timeout_info) {
    // create a new thread to kill the timeout process
    pid_t pid = ((struct timeout_info *)timeout_info)->pid;
    rlim_t timeout = ((struct timeout_info *)timeout_info)->timeout;

    // pthread_detach(pthread_self()) set the thread's status to be unjoinable to release resources; if success, return 0
    if (pthread_detach(pthread_self()) != 0 || sleep((unsigned int)((timeout + 1000) / 1000)) != 0) {
        KillProcess(pid);
        return NULL;
    }

    // check in the end
    if (KillProcess(pid) != 0)
        return NULL;
    return NULL;
}
void init_result(struct result *_result) {
    _result->cpu_time = 0;
    _result->real_time = 0;
    _result->memory = 0;
    _result->signal = 0;
    _result->exit_code = 0;
    _result->result = 0;
}
void print_config(const struct config* _config) {
    printf("Config:\n");
    printf("max_cpu_time: %ld\n", _config->max_cpu_time);
    printf("max_real_time: %ld\n", _config->max_real_time);
    printf("max_memory: %ld\n", _config->max_memory);
    printf("max_stack: %ld\n", _config->max_stack);
    printf("max_process_number: %ld\n", _config->max_process_number);
    printf("max_output_size: %ld\n", _config->max_output_size);

    printf("exe_type: %d\n", _config->exe_type);
    printf("exe_path: %s\n", _config->exe_path ? _config->exe_path : "NULL");
    printf("input_path: %s\n", _config->input_path ? _config->input_path : "NULL");
    printf("output_path: %s\n", _config->output_path ? _config->output_path : "NULL");
    printf("error_path: %s\n", _config->error_path ? _config->error_path : "NULL");

    printf("exe_args:\n");

    for (int i = 0; i < MAX_ARG + 1 &&_config->exe_args[i]; ++i) {
        printf("  [%d]: %s\n", i, _config->exe_args[i]);
    }

    printf("exe_envs:\n");
    for (int i = 0; i < MAX_ENV + 1 && _config->exe_envs[i]; ++i) {
        printf("  [%d]: %s\n", i, _config->exe_envs[i]);
    }

    printf("uid: %d\n", _config->uid);
    printf("gid: %d\n", _config->gid);
}
void run(struct config* _config,struct result* _result) {
    int status;
    pid_t child_pid;
    struct timeval start,end;
    struct rusage resource_usage;

    // initialize log
    check_args(_config,_result);
    init_result(_result);
    gettimeofday(&start, NULL);

    child_pid = fork();
    if(child_pid == 0) {
        // child process content
        // run child process task
        run_child_process(_config,_result);
    } else if(child_pid > 0){
        // parent process content
        stat_resource_consumption(child_pid,_config,_result,&resource_usage,&status);
        generate_result(_config,_result,&resource_usage,&status,&start,&end);
    } else {
        printf("error");
    }
}
jobject get_result_object(JNIEnv *env, struct result res) {
    // 查找 Result 的运行时类型
    jclass resultClass = (*env)->FindClass(env, "com/zhc/sandbox/Result");
    // 获取 Result 类的无参构造方法
    jmethodID resultConstructor = (*env)->GetMethodID(env, resultClass, "<init>", "()V");
    // 创建 Result 类的对象
    jobject jResult = (*env)->NewObject(env, resultClass, resultConstructor);

    // 获取 setter 方法
    jmethodID setCpuTimeMethod = (*env)->GetMethodID(env, resultClass, "setCpuTime", "(J)V");
    jmethodID setRealTimeMethod = (*env)->GetMethodID(env, resultClass, "setRealTime", "(J)V");
    jmethodID setMemoryMethod = (*env)->GetMethodID(env, resultClass, "setMemory", "(J)V");
    jmethodID setSignalMethod = (*env)->GetMethodID(env, resultClass, "setSignal", "(I)V");
    jmethodID setExitCodeMethod = (*env)->GetMethodID(env, resultClass, "setExitCode", "(I)V");
    jmethodID setResultMethod = (*env)->GetMethodID(env, resultClass, "setResult", "(I)V");
    jmethodID setOutputMethod = (*env)->GetMethodID(env, resultClass, "setOutput", "(Ljava/lang/String;)V");



    // 获取结构体的值
    jlong cpuTime = res.cpu_time;
    jlong realTime = res.real_time;
    jlong memory = res.memory;
    jint signal = res.signal;
    jint exitCode = res.exit_code;
    jint result = res.result;
    jstring output = (*env)->NewStringUTF(env, res.output);

    // 设置 Result 对象的值
    (*env)->CallVoidMethod(env, jResult, setCpuTimeMethod, cpuTime);
    (*env)->CallVoidMethod(env, jResult, setRealTimeMethod, realTime);
    (*env)->CallVoidMethod(env, jResult, setMemoryMethod, memory);
    (*env)->CallVoidMethod(env, jResult, setSignalMethod, signal);
    (*env)->CallVoidMethod(env, jResult, setExitCodeMethod, exitCode);
    (*env)->CallVoidMethod(env, jResult, setResultMethod, result);
    (*env)->CallVoidMethod(env, jResult, setOutputMethod, output);


    return jResult;
}
char *convert_jstring_to_cstring(JNIEnv *env, jstring jStr) {
    if (jStr == NULL) {
        return NULL;
    }
    const char *temp = (*env)->GetStringUTFChars(env, jStr, NULL);
    if (temp == NULL) {
        // 内存不足
        return NULL;
    }
    char *cStr = strdup(temp);
    (*env)->ReleaseStringUTFChars(env, jStr, temp);
    return cStr;
}
void direct(const char *in, const char *out, const char *err) {
    int in_fd, out_fd, err_fd;
    // 打开输入文件并重定向标准输入
    if (in) {
        in_fd = open(in, O_RDONLY);
        if (in_fd == -1) {
            perror("Failed to open input file");
            exit(EXIT_FAILURE);
        }
        if (dup2(in_fd, STDIN_FILENO) == -1) {
            perror("Failed to redirect standard input");
            close(in_fd);
            exit(EXIT_FAILURE);
        }
        close(in_fd); // 重定向后关闭文件描述符
    }

    // 打开输出文件并重定向标准输出
    if (out) {
        out_fd = open(out, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (out_fd == -1) {
            perror("Failed to open output file");
            exit(EXIT_FAILURE);
        }
        if (dup2(out_fd, STDOUT_FILENO) == -1) {
            perror("Failed to redirect standard output");
            close(out_fd);
            exit(EXIT_FAILURE);
        }
        close(out_fd); // 重定向后关闭文件描述符
    }

    // 打开错误输出文件并重定向标准错误
    if (err) {
        err_fd = open(err, O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (err_fd == -1) {
            perror("Failed to open error file");
            exit(EXIT_FAILURE);
        }
        if (dup2(err_fd, STDERR_FILENO) == -1) {
            perror("Failed to redirect standard error");
            close(err_fd);
            exit(EXIT_FAILURE);
        }
        close(err_fd); // 重定向后关闭文件描述符
    }
}
void check_args(struct config* _config,struct result* _result) {
    // current user must be root
    if (getuid() != 0){
        return;
    }

    // check config
    if (_config->max_cpu_time < 1 || _config->max_real_time < 1 || _config->max_stack < 1 ||
        _config->max_memory < 1 || _config->max_process_number < 1 || _config->max_output_size < 1) {
        return;
    }
}
void run_child_process(struct config* _config,struct result* _result) {
    // 限制资源开销
    struct rlimit max_cpu_time;
    struct rlimit max_memory;
    struct rlimit max_stack;
    struct rlimit max_process_number;
    struct rlimit max_output_size;

    // xx/s
    max_cpu_time.rlim_cur = max_cpu_time.rlim_max = (rlim_t)((_config->max_cpu_time + 1000) / 1000);
    if(setrlimit(RLIMIT_CPU,&max_cpu_time) != 0) {
        // error exit
        exit(EXIT_FAILURE);
    }

    // ? * 2
    max_memory.rlim_cur = max_memory.rlim_max = (rlim_t)(rlim_t)(_config->max_memory) * 2;
    if(setrlimit(RLIMIT_AS,&max_memory) != 0) {
        // error exit
        printf("error exit for jvm memory");
        exit(EXIT_FAILURE);
    }

    max_stack.rlim_cur = max_stack.rlim_max = (rlim_t)(_config->max_stack);
    if (setrlimit(RLIMIT_STACK, &max_stack) != 0) {
        exit(EXIT_FAILURE);
    }

    max_output_size.rlim_cur = max_output_size.rlim_max = (rlim_t)_config->max_output_size;
    if (setrlimit(RLIMIT_FSIZE, &max_output_size) != 0) {
        exit(EXIT_FAILURE);
    }

    max_process_number.rlim_cur = max_process_number.rlim_max = (rlim_t)_config->max_process_number;
    if (setrlimit(RLIMIT_NPROC, &max_process_number) != 0) {
        exit(EXIT_FAILURE);
    }
    // set gid
    gid_t group_list[] = {_config->gid};
    if (_config->gid != -1 && (setgid(_config->gid) == -1 || getgroups(sizeof(group_list) / sizeof(gid_t), group_list) == -1)) {
        exit(EXIT_FAILURE);
    }

    // set uid (不给子进程 root 用户的权限) 但是执行java程序似乎需要这个权限
    //    if (_config->uid != -1 && setuid(_config->uid) == -1) {
    //        exit(EXIT_FAILURE);
    //    }

    load_jvm_seccomp();
    direct(_config->input_path,_config->output_path,_config->error_path);
    execve(_config->exe_path,_config->exe_args,_config->exe_envs);
    perror("execve failed");
}
void parse_config(JNIEnv *env, jobject jConfig, struct config *_config) {
    // 获取 Config 类
    jclass configClass = (*env)->GetObjectClass(env, jConfig);

    // file io method
    jmethodID getInputPathMethod = (*env)->GetMethodID(env, configClass, "getInputPath", "()Ljava/lang/String;");
    jstring jInputPath = (jstring)(*env)->CallObjectMethod(env, jConfig, getInputPathMethod);

    jmethodID getOutputPathMethod = (*env)->GetMethodID(env, configClass, "getOutputPath", "()Ljava/lang/String;");
    jstring jOutputPath = (jstring)(*env)->CallObjectMethod(env, jConfig, getOutputPathMethod);

    jmethodID getErrorPathMethod = (*env)->GetMethodID(env, configClass, "getErrorPath", "()Ljava/lang/String;");
    jstring jErrorPath = (jstring)(*env)->CallObjectMethod(env, jConfig, getErrorPathMethod);

    // exe method
    jmethodID getExeTypeMethod = (*env)->GetMethodID(env, configClass, "getExeType", "()I");
    jint jexeType = (*env)->CallIntMethod(env, jConfig, getExeTypeMethod);

    jmethodID getExePathMethod = (*env)->GetMethodID(env, configClass, "getExePath", "()Ljava/lang/String;");
    jstring jexePath = (jstring)(*env)->CallObjectMethod(env, jConfig, getExePathMethod);

    jmethodID getExeArgsMethod = (*env)->GetMethodID(env, configClass, "getExeArgs", "()[Ljava/lang/String;");
    jobjectArray jexeArgs = (*env)->CallObjectMethod(env, jConfig, getExeArgsMethod);

    jmethodID getExeEnvsMethod = (*env)->GetMethodID(env, configClass, "getExeEnvs", "()[Ljava/lang/String;");
    jobjectArray jexeEnvs = (jstring)(*env)->CallObjectMethod(env, jConfig, getExePathMethod);

    // limit
    jmethodID getMaxRealTimeMethod = (*env)->GetMethodID(env, configClass, "getMaxRealTime", "()J");
    jlong jmaxRealTime = (*env)->CallLongMethod(env, jConfig, getMaxRealTimeMethod);

    jmethodID getMaxCpuTimeMethod = (*env)->GetMethodID(env, configClass, "getMaxCpuTime", "()J");
    jlong jmaxCpuTime = (*env)->CallLongMethod(env, jConfig, getMaxCpuTimeMethod);

    jmethodID getMaxMemoryMethod = (*env)->GetMethodID(env, configClass, "getMaxMemory", "()J");
    jlong jmaxMemory = (*env)->CallLongMethod(env, jConfig, getMaxMemoryMethod);

    jmethodID getMaxStackSizeMethod = (*env)->GetMethodID(env, configClass, "getMaxStackSize", "()J");
    jlong jmaxStackSize = (*env)->CallLongMethod(env, jConfig, getMaxStackSizeMethod);

    jmethodID getMaxOutputSizeMethod = (*env)->GetMethodID(env, configClass, "getMaxOutputSize", "()J");
    jlong jmaxOutputSize = (*env)->CallLongMethod(env, jConfig, getMaxOutputSizeMethod);

    jmethodID getMaxProcessNumberMethod = (*env)->GetMethodID(env, configClass, "getMaxProcessNumber", "()I");
    jint jmaxProcessNumber = (*env)->CallIntMethod(env, jConfig, getMaxProcessNumberMethod);


    // TODO 将 jni jobjectArray java_command 转换成 c 语言的 char** cmd ，并且设置到 config 结构体的command属性内
    jsize len = (*env)->GetArrayLength(env, jexeArgs);
    jstring jstr;
    for(int i = 0;i < len;++i) {
    }
    for (int i = 0; i < MAX_ARG + 1; i++) {
        if (i < len) {
            jstr = (*env)->GetObjectArrayElement(env, jexeArgs, i);
            _config->exe_args[i] = convert_jstring_to_cstring(env, jstr);
        } else {
            _config->exe_envs[i] = NULL;
        }
    }

    jsize env_len = (*env)->GetArrayLength(env, jexeEnvs);
    jstring env_jstr;
    for (int i = 0; i < MAX_ENV + 1; i++) {
        if (i < env_len) {
            env_jstr = (*env)->GetObjectArrayElement(env, jexeEnvs, i);
            _config->exe_envs[i] = convert_jstring_to_cstring(env, env_jstr);
        } else {
            _config->exe_envs[i] = NULL;
        }
    }

    _config->input_path = convert_jstring_to_cstring(env, jInputPath);
    _config->output_path = convert_jstring_to_cstring(env, jOutputPath);
    _config->error_path = convert_jstring_to_cstring(env, jErrorPath);

    _config->exe_type = (int) jexeType;
    _config->exe_path = convert_jstring_to_cstring(env, jexePath);

    _config->max_real_time = (long) jmaxRealTime;
    _config->max_cpu_time = (long) jmaxCpuTime;
    _config->max_memory = (long) jmaxMemory;
    _config->max_stack = (long) jmaxStackSize;
    _config->max_process_number = (int) jmaxProcessNumber;
    _config->max_output_size = (long)jmaxOutputSize;
}
void stat_resource_consumption(pid_t child_pid, struct config *_config, struct result *_result, struct rusage *resource_usage, int *status) {
    pthread_t tid = 0;
    struct timeout_info timeout = {child_pid,_config->max_real_time};
    if (pthread_create(&tid, NULL, timeout_killer, (void *)(&timeout)) != 0)
    {
        perror("pthread_create");
        KillProcess(child_pid);
        exit(EXIT_FAILURE);
    }

    if (wait4(child_pid, status, WSTOPPED, resource_usage) == -1)
    {
        perror("wait4");
        KillProcess(child_pid);
        exit(EXIT_FAILURE);
    }

    // process exited, we may need to cancel timeout killer thread
    if (LIMITED(_config->max_real_time))
        pthread_cancel(tid);
}
void generate_result(struct config *_config, struct result *_result, struct rusage *resource_usage, int *status, struct timeval *start, struct timeval *end){
    // get end time
    gettimeofday(end, NULL);
    _result->real_time = (uint32_t)(end->tv_sec * 1000 + end->tv_usec / 1000 - start->tv_sec * 1000 - start->tv_usec / 1000);

    // if the child process terminated because it received a signal that was not handled, acquire the signal code
    if (WIFSIGNALED(*status) != 0)
        _result->signal = WTERMSIG(*status);

    if (_result->signal == SIGUSR1)
    {
        _result->result = SYSTEM_ERROR;
    }
    else
    {
        _result->exit_code = WEXITSTATUS(*status);
        _result->cpu_time = (uint32_t)(resource_usage->ru_utime.tv_sec * 1000 +
                                       resource_usage->ru_utime.tv_usec / 1000);
        _result->memory = (uint64_t)(resource_usage->ru_maxrss * 1024);

        _result->output = read_file(_config->output_path);

        if (_result->exit_code != 0)
            _result->result = RUNTIME_ERROR;

        if (_result->signal == SIGSEGV)
        {
            if (LIMITED(_config->max_memory) && _result->memory > _config->max_memory)
                _result->result = MEMORY_LIMIT_EXCEEDED;
            else
                _result->result = RUNTIME_ERROR;
        }
        else if (_result->signal == SIGXFSZ)
        {
            _result->result = OUTPUT_LIMIT_EXCEEDED;
        }
        else
        {
            if (_result->signal != 0)
                _result->result = RUNTIME_ERROR;

            if (LIMITED(_config->max_memory) && _result->memory > _config->max_memory)
                _result->result = MEMORY_LIMIT_EXCEEDED;

            if (LIMITED(_config->max_real_time) && _result->real_time > _config->max_real_time)
                _result->result = REAL_TIME_LIMIT_EXCEEDED;

            if (LIMITED(_config->max_cpu_time) && _result->cpu_time > _config->max_cpu_time)
                _result->result = CPU_TIME_LIMIT_EXCEEDED;
        }
    }
}